package com.jsits.v2x.common.timer;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;
import java.util.*;

/**
 * 定时器管理类
 * 用于开启、关闭定时器
 */
public class TimerMgr
{
	/*
	 * 日志
	 */
	private static Logger logger = LoggerFactory.getLogger(TimerMgr.class);
	
	/*
	 * 当前定时器ID
	 */
	private int timerID;
	
	/*
	 * 周期循环用户定时器数据
	 */
	private int[] timerData;
	
	/*
	 * 定时器启动类
	 */
	private Map<Integer, Timer> timerMap;
	
	/*
	 * 定时器任务信息
	 */
	private Map<Integer, TimerTaskInfo> taskMap;
	
	/*
	 * 周期性执行定时器最大个数
	 */	
	private static final int MAX_PER_TIMER_NUM = 100;
	
	/*
	 * 定期器ID最大值
	 */
	private static final int MAX_TIMER_ID = 99999;
	
	/*
	 * 单例对象
	 */
	private static TimerMgr timerMgr = null;
	
	/*
	 * 私有化构造函数，保证单例
	 */
	private TimerMgr() 
	{
		timerMap = new HashMap<Integer, Timer>();
		taskMap = new HashMap<Integer, TimerTaskInfo>();
		timerData = new int[MAX_PER_TIMER_NUM];
		for (int i = 0; i < MAX_PER_TIMER_NUM; i++) 
		{
			timerData[i] = 0;
		}		
	}
	
	/*
	 * 获取实例入口
	 */
	public static TimerMgr getInstance()
	{
		if (timerMgr == null)
		{
			timerMgr = new TimerMgr();
		}
		return timerMgr;
	}
	
	/*
	 * 自定义定时器执行任务
	 */
	private class TimerTaskInfo extends TimerTask 
	{
		//保存每个定时器对应的系统定时器ID和用户定时器ID，供回调时使用
		private int timerId;
		private Object object;
		private String methodName;
		private boolean isOnce = true;
		
		//定时器到达时调用的接口方法
		public void run() 
		{
			//如果是一次性执行定时器，删除保存的对应定时器信息
			if (isOnce) 
			{
				taskMap.remove(timerId);
			}
			try
			{
				Method method = getMethod();
				method.invoke(object);
			}
			catch(Throwable e)
			{
				logger.error(e.getMessage());
			}			
		}
		
		//获取方法
		private Method getMethod()
		{
			Method method = null;
			for (Class<?> clazz = object.getClass(); clazz != Object.class; clazz = clazz.getSuperclass())
			{
				try
				{
					method = clazz.getDeclaredMethod(methodName);
					return method;
				}
				catch (Exception e)
				{
				}					
			}
			return null;
		}
	}
	
	/*
	 * 开启定时器
	 */
	public int setTimer(Object object, String method, int period, boolean isOnce) 
	{	
		TimerTaskInfo timerTask = new TimerTaskInfo();
		Timer timer = new Timer();
		
		//将系统定时器ID和用户定时器ID写入定时任务
		timerTask.timerId = getNextTimerId(isOnce);
		timerTask.object = object;
		timerTask.methodName = method;
		timerTask.isOnce = isOnce;
		
		//开启定时器任务
		if (isOnce) 
		{
			timer.schedule(timerTask, period);
		}
		else 
		{
			timer.scheduleAtFixedRate(timerTask, period, period);
		}
		//将新建的定时器信息放入定时器信息map
		timerMap.put(timerTask.timerId, timer);
		taskMap.put(timerTask.timerId, timerTask);
		
		return timerTask.timerId;
	}

	/**
	 * 通过首次启动时间设置定时器
	 * 
	 * @author  zlm
	 * @param object 回调对象
	 * @param method 回调方法
	 * @param firstTime 首次启动时间
	 * @param period 两次间隔时间（毫秒）
	 * @param isOnce 是否为单次定时器，如果是则period参数无效
	 * @return 定时器ID
	 * @see [类、类#方法、类#成员]
	 */
	public int setTimer(Object object, String method, Date firstTime, int period, boolean isOnce) 
	{
		TimerTaskInfo timerTask = new TimerTaskInfo();
		Timer timer = new Timer();
		
		//将系统定时器ID和用户定时器ID写入定时任务
		timerTask.timerId = getNextTimerId(isOnce);
		timerTask.object = object;
		timerTask.methodName = method;
		timerTask.isOnce = isOnce;
		
		//开启定时器任务
		if (isOnce) 
		{
			timer.schedule(timerTask, firstTime);
		}
		else 
		{
			timer.scheduleAtFixedRate(timerTask, firstTime, period);
		}
		//将新建的定时器信息放入定时器信息map
		timerMap.put(timerTask.timerId, timer);
		taskMap.put(timerTask.timerId, timerTask);
		
		return timerTask.timerId;
	}

	/*
	 * 关闭定时器
	 */
	public void killTimer(int timerID) 
	{
		//停止系统定时器ID对应的定时启动类
		Timer timer = timerMap.get(timerID);
		if (timer != null) 
		{
			timer.cancel();
		}
		timerMap.remove(timerID);
		
		//停止系统定时器ID对应的定时器任务
		TimerTaskInfo timerTask = taskMap.get(timerID);
		if (timerTask != null) 
		{
			timerTask.cancel();			
		}
		taskMap.remove(timerID);
		
		//周期循环定时器,需置空对应数据
		if (MAX_PER_TIMER_NUM >= timerID) 
		{
			timerData[timerID - 1] = 0;
		}
	}
	
	/*
	 * 获取一次性执行定时器ID
	 */
	private int getNextTimerId() 
	{
		if (timerID < MAX_TIMER_ID) 
		{
			timerID++;
		}
		else
		{
			//一次性定时器ID需要与周期性定时器错开
			timerID = MAX_PER_TIMER_NUM + 1;
		}
		return timerID;
	}
	
	/**
	 * 获取定时器ID
	 * 
	 * @author  zlm
	 * @param isOnce 是否为单次定时器（仅执行一次）
	 * @return 定时器ID
	 * @see [类、类#方法、类#成员]
	 */
	private int getNextTimerId(boolean isOnce)
	{
		int timerID = 0;
		//一次性定时器ID通过流水号获取
		if (isOnce) 
		{
			timerID = getNextTimerId();
		}
		else 
		{
			//周期性定时器通过数组获取，遍历数组，如果用户数据为0，表示可用
			for (int i = 0; i < MAX_PER_TIMER_NUM; i++) 
			{
				if (timerData[i] == 0) 
				{
					timerData[i] = 1;
					timerID = i + 1;
					break;
				}
			}
			if (timerID == 0) 
			{
				logger.error("Set periodic timer failed, exceed max num, can't set more");
			}
		}
		
		return timerID;
	}
}
